#!/usr/bin/python
#
# Copyright 2011 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""This code creates new ad units from a text file"""

__author__ = 'felipel+bulk@google.com (Felipe Lora)'

# Locate the client library. If module was installed via "setup.py" script, then
# the following two lines are not needed.
import os
import sys
import time
sys.path.insert(0, os.path.join('..', '..', '..', '..'))

# Import appropriate classes from the client library.
from adspygoogle.common import Utils
from adspygoogle.dfp.DfpClient import DfpClient
from adspygoogle.dfp import DfpUtils
from adspygoogle.dfp.DfpErrors import *

# Initialize client object.
client = DfpClient(path=os.path.join('..', '..', '..', '..'))

# Initialize appropriate service. By default, the request is always made against
# the production environment.
inventory_service = client.GetInventoryService('https://www.google.com', 'v201203')
network_service = client.GetNetworkService('https://www.google.com', 'v201203')

# Global container of network's ad units
networkAdUnits = []

# Get the root network ID
network_root_id = network_service.GetCurrentNetwork()[0]['effectiveRootAdUnitId']

# Define names of logs
warningLog = "warnings.log"
errorLog = "errors.log"
successLog = "success.log"

# Choose your target platform, WEB for desktop and MOBILE for mobile
TARGETPLATFORM = 'WEB'

#############################################################

# Get all the ad units from the network and add their level
def getNetworkAdUnits():
  global networkAdUnits
  networkAdUnits = DfpUtils.GetAllEntitiesByStatementWithService(inventory_service)
  id_indexer = dict((n['id'], i) for i,n in enumerate(networkAdUnits))


  for ad_unit in networkAdUnits:
    if ad_unit['status'] == 'ACTIVE':
      adUnitLevel = getAdUnitLevel(ad_unit, networkAdUnits, id_indexer)
      ad_unit['level'] = adUnitLevel


# Return a value from 1 to 5 indicating the current level for the ad unit (needs to be an existing one)
def getAdUnitLevel(ad_unit, networkAdUnits, id_indexer):
  level = 1
  if ad_unit['id'] == network_root_id: #Handle the root level and give it a 0
    return 0
  if ad_unit.has_key("parentId"):
    currId = ad_unit['parentId']
    while currId != network_root_id:
      level = level + 1
      # Now find the parentId of the current parent
      parentAdUnitIndex = id_indexer.get(currId)
      if parentAdUnitIndex != None:
        currId = networkAdUnits[parentAdUnitIndex]['parentId']
      else:
        level = 5
        break
    return level
  else:
   return 0



# Get parentId for current ad unit according to it's level
def getParentId(ad_unit,level):
  # Create a statement to select the 1st level ad unit of the tree where the ad unit to create belongs
  values = [{
      'key': 'id',
      'value': {
          'xsi_type': 'TextValue',
          'value': network_root_id
      }
   },
   {  'key': 'auName',
      'value': {
          'xsi_type': 'TextValue',
          'value': ad_unit['parent1stLevelName']
      }
   }
  ]
  filter_statement = {'query': 'WHERE parentId = :id AND name = :auName','values': values}
 
  tempSLAU = inventory_service.GetAdUnitsByStatement(filter_statement)
  if tempSLAU[0]['totalResultSetSize'] == "0":
    f = open(errorLog,'a')
    m = "%s,%s,Ad unit not created because its parent first level ad unit does not exist\n" % (level,ad_unit['name'])
    f.write(m)
    f.close()
    return ad_unit
  else:
    firstLevelAdUnit = tempSLAU[0]['results'] # Possibly want to validate here that there is only one element returned

    if level == 2:
      ad_unit['parentId'] = firstLevelAdUnit[0]['id']
      return ad_unit     

    elif level == 3:
      # Find the id of the second level ad unit
      values3rdlevel = [{
         'key': 'id',
          'value': {
              'xsi_type': 'TextValue',
              'value': firstLevelAdUnit[0]['id']
          }
       },
       {  'key': 'auName',
          'value': {
              'xsi_type': 'TextValue',
              'value': ad_unit['parent2ndLevelName']
          }
       }
      ]

      filter_statement = {'query': 'WHERE parentId = :id AND name = :auName','values': values3rdlevel}
      tempSLAU = inventory_service.GetAdUnitsByStatement(filter_statement)
      if tempSLAU[0]['totalResultSetSize'] == "0":
        f = open(errorLog,'a')
        m = "%s,%s,Ad unit not created because its parent second level ad unit does not exist\n" % (level,ad_unit['name'])
        f.write(m)
        f.close()
      else:
        secondLevelAdUnit = tempSLAU[0]['results']
        ad_unit['parentId'] = secondLevelAdUnit[0]['id']
      return ad_unit

    elif level == 4:
      # Find the id of the second level ad unit
      values3rdlevel = [{
         'key': 'id',
          'value': {
              'xsi_type': 'TextValue',
              'value': firstLevelAdUnit[0]['id']
          }
       },
       {  'key': 'auName',
          'value': {
              'xsi_type': 'TextValue',
              'value': ad_unit['parent2ndLevelName']
          }
       }
      ]
      filter_statement = {'query': 'WHERE parentId = :id AND name = :auName','values': values3rdlevel}

      tempSLAU = inventory_service.GetAdUnitsByStatement(filter_statement)
      if tempSLAU[0]['totalResultSetSize'] == "0":
        f = open(errorLog,'a')
        m = "%s,%s,Ad unit not created because its parent second level ad unit does not exist\n" % (level,ad_unit['name'])
        f.write(m)
        f.close()
        return ad_unit
      else:
        secondLevelAdUnit = tempSLAU[0]['results']
      # Find the id of the third level ad unit
      values4thlevel = [{
         'key': 'id',
          'value': {
              'xsi_type': 'TextValue',
              'value': secondLevelAdUnit[0]['id']
          }
       },
       {  'key': 'auName',
          'value': {
              'xsi_type': 'TextValue',
              'value': ad_unit['parent3rdLevelName']
          }
       }
      ]
      filter_statement = {'query': 'WHERE parentId = :id AND name = :auName','values': values4thlevel}

      tempSLAU = inventory_service.GetAdUnitsByStatement(filter_statement)
      if tempSLAU[0]['totalResultSetSize'] == "0":
        f = open(errorLog,'a')
        m = "%s,%s,Ad unit not created because its parent third level ad unit does not exist\n" % (level,ad_unit['name'])
        f.write(m)
        f.close()
        return ad_unit
      else:
        thirdLevelAdUnit = tempSLAU[0]['results']
        ad_unit['parentId'] = thirdLevelAdUnit[0]['id']
        return ad_unit

    elif level == 5:
      # Find the id of the second level ad unit
      values3rdlevel = [{
         'key': 'id',
          'value': {
              'xsi_type': 'TextValue',
              'value': firstLevelAdUnit[0]['id']
          }
       },
       {  'key': 'auName',
          'value': {
              'xsi_type': 'TextValue',
              'value': ad_unit['parent2ndLevelName']
          }
       }
      ]
      filter_statement = {'query': 'WHERE parentId = :id AND name = :auName','values': values3rdlevel}
      
      tempSLAU = inventory_service.GetAdUnitsByStatement(filter_statement)
      if tempSLAU[0]['totalResultSetSize'] == "0":
        f = open(errorLog,'a')
        m = "%s,%s,Ad unit not created because its parent second level ad unit does not exist\n" % (level,ad_unit['name'])
        f.write(m)
        f.close()
        return ad_unit
      else:
        secondLevelAdUnit = tempSLAU[0]['results']
 
      # Find the id of the third level ad unit
      values4thlevel = [{
         'key': 'id',
          'value': {
              'xsi_type': 'TextValue',
              'value': secondLevelAdUnit[0]['id']
          }
       },
       {  'key': 'auName',
          'value': {
              'xsi_type': 'TextValue',
              'value': ad_unit['parent3rdLevelName']
          }
       }
      ]
      filter_statement = {'query': 'WHERE parentId = :id AND name = :auName','values': values4thlevel}

      tempSLAU = inventory_service.GetAdUnitsByStatement(filter_statement)
      if tempSLAU[0]['totalResultSetSize'] == "0":
        f = open(errorLog,'a')
        m = "%s,%s,Ad unit not created because its parent third level ad unit does not exist\n" % (level,ad_unit['name'])
        f.write(m)
        f.close()
        return ad_unit
      else:
        thirdLevelAdUnit = tempSLAU[0]['results']

      # Find the id of the fourth level ad unit
      values5thlevel = [{
         'key': 'id',
          'value': {
              'xsi_type': 'TextValue',
              'value': thirdLevelAdUnit[0]['id']
          }
       },
       {  'key': 'auName',
          'value': {
              'xsi_type': 'TextValue',
              'value': ad_unit['parent4thLevelName']
          } 
       }
      ]
      filter_statement = {'query': 'WHERE parentId = :id AND name = :auName','values': values5thlevel}

      tempSLAU = inventory_service.GetAdUnitsByStatement(filter_statement)
      if tempSLAU[0]['totalResultSetSize'] == "0":
        f = open(errorLog,'a')
        m = "%s,%s,Ad unit not created because its parent fourth level ad unit does not exist\n" % (level,ad_unit['name'])
        f.write(m)
        f.close()
        return ad_unit
      else:
        fourthLevelAdUnit = tempSLAU[0]['results']
      
      ad_unit['parentId'] = fourthLevelAdUnit[0]['id']
      return ad_unit

# Create ad units and update networkAdUnits with their ID's

def createAUs(adUnits,level):
  ad_units_to_create = []
  validator = 1
  for ad_unit in adUnits:
    if ad_unit['level'] == level:
      if level == 1:
        ad_unit['parentId'] = network_root_id
        validator = validateExistance(ad_unit, level)
        del ad_unit['level']
      elif level == 2:
        ad_unit = getParentId(ad_unit,level)
        validator = validateExistance(ad_unit, level)
        del ad_unit['level']
        del ad_unit['parent1stLevelName']
      elif level == 3:
        ad_unit = getParentId(ad_unit,level)
        validator = validateExistance(ad_unit, level)
        del ad_unit['level']
        del ad_unit['parent1stLevelName'] 
        del ad_unit['parent2ndLevelName']
      elif level == 4:
        ad_unit = getParentId(ad_unit,level)
        validator = validateExistance(ad_unit, level)
        del ad_unit['level']
        del ad_unit['parent1stLevelName'] 
        del ad_unit['parent2ndLevelName'] 
        del ad_unit['parent3rdLevelName'] 
      elif level == 5:
        ad_unit = getParentId(ad_unit,level)
        validator = validateExistance(ad_unit, level)
        del ad_unit['level']
        del ad_unit['parent1stLevelName'] 
        del ad_unit['parent2ndLevelName'] 
        del ad_unit['parent3rdLevelName'] 
        del ad_unit['parent4thLevelName'] 
      
      if ad_unit['parentId'] != '0' and validator == 1:
        ad_units_to_create.append(ad_unit)

    # Sleep in order not to throttle the quote limitations of the API
    time.sleep(0.1)
  
  # Create the ad units  
  print "Creating %s %s-level ad units" % (str(len(ad_units_to_create)), str(level))
  try:
    ad_units_created = inventory_service.CreateAdUnits(ad_units_to_create)
    fs = open(successLog,'a')
    for auCreated in ad_units_created:
      m = "%s,%s,%s\n" % (auCreated['parentId'],auCreated['id'],auCreated['name'])
      fs.write(m)      
    fs.close()

  except DfpRequestError,e:
    if "UniqueError.NOT_UNIQUE" in e.fault_string:
      warningLogFile = open(warningLog,'a')
      # Log format: level,ad unit name,message
      message = "Ouch - DUPLICATE AD UNIT - This message should not have appeared! : %s \n" % e.fault_string 
      warningLogFile.write(message)
      warningLogFile.close()
    else:
      warningLogFile = open(warningLog,'a')
      # Log format: level,ad unit name,message
      message = "%s\n"  % (e.fault_string)
      warningLogFile.write(message)
      warningLogFile.close()


  # Update list of created networks
  getNetworkAdUnits()

 
# Read all the ad units to be created from the DoubleClick standard text file
def getFileAdUnits(auFile,ctr):
  getNetworkAdUnits()

  adUnits = []
  fauFile = open(auFile,'rU')
  sizeValidator = 1
  nameValidator = 1

  for line in fauFile.readlines():
    adUnit = { 'parentId' : '0' }  
    currLine = line.rsplit("^")
    
    names = currLine[0].rsplit(",")
    sizes = currLine[1].rsplit(",")
    target = currLine[2]
    expTargeted = currLine[3].rsplit("\n")[0]

    
    adUnit['name'] = names[len(names)-1]
    adUnit['level'] = len(names)

    if len(names) == 1:
      nameValidator = validateAdUnitName(adUnit,ctr)

    if len(names) == 2:
      nameValidator = validateAdUnitName(adUnit,ctr)
      adUnit['parent1stLevelName'] = names[len(names)-2]
    elif len(names) == 3:
      nameValidator = validateAdUnitName(adUnit,ctr)
      adUnit['parent1stLevelName'] = names[len(names)-3]
      adUnit['parent2ndLevelName'] = names[len(names)-2]
    elif len(names) == 4:
      nameValidator = validateAdUnitName(adUnit,ctr)
      adUnit['parent1stLevelName'] = names[len(names)-4]
      adUnit['parent2ndLevelName'] = names[len(names)-3]
      adUnit['parent3rdLevelName'] = names[len(names)-2]
    elif len(names) == 5:
      nameValidator = validateAdUnitName(adUnit,ctr)
      adUnit['parent1stLevelName'] = names[len(names)-5]
      adUnit['parent2ndLevelName'] = names[len(names)-4]
      adUnit['parent3rdLevelName'] = names[len(names)-3]
      adUnit['parent4thLevelName'] = names[len(names)-2]

    adUnit['description'] = ''
    adUnit['targetWindow'] = target.upper()
    adUnit['adUnitSizes'] = []

    adUnit['targetPlatform'] = TARGETPLATFORM

    for s in sizes:
      currSize = { 'environmentType' : "BROWSER" }
      if 'X' in s.upper():
        widthHeight = s.upper().rsplit("X")
        isAspectRatio = 'false'
      if ':' in s:
        widthHeight = s.rsplit(":")
        isAspectRatio = 'true'

      if s != '':  
        if widthHeight[0].isdigit() and widthHeight[1].isdigit():
          currSize['size'] = { 'width' : widthHeight[0], 'isAspectRatio' : isAspectRatio, 'height' : widthHeight[1] }
          adUnit['adUnitSizes'].append(currSize)
        else: 
          sizeValidator = 0
          f = open(errorLog,'a')
          m = "%s,%s,one of the sizes for this ad unit is not a number\n" % (adUnit['level'], adUnit['name'])
          if ctr == 1:
            f.write(m)
          f.close()
  
    adUnit['explicitlyTargeted'] = expTargeted
   
    if (nameValidator == 1) and (sizeValidator == 1):
      adUnits.append(adUnit)

  return adUnits

def validateAdUnitName(adUnit,ctr):
  f = open(errorLog,'a')
  flag = 1
  m = ""
  # Validate name:
  allowed = set('_-.*()!<>/\\\"');
  if len(adUnit['name']) > 100:
    m = "%s,%s,The length of the name exceeds 100 characters\n" % (adUnit['level'], adUnit['name'])
    flag = 0

  if adUnit['name'].isalnum():
    flag = flag
  else:
      for c in adUnit['name']:      
        if not c.isalnum() and c not in allowed:
          flag = 0
          m = m + "%s, %s, The name contains invalid characters\n" % (adUnit['level'], adUnit['name'])

  if ctr == 1:
    f.write(m)
  f.close()      
  return flag

def validateExistance(ad_unit,level):
  # Validate existing ad units
  values = [{
    'key': 'id',
    'value': {
        'xsi_type': 'TextValue',
        'value': ad_unit['parentId']
    }
   },
  {  'key': 'auName',
    'value': {
        'xsi_type': 'TextValue',
        'value': ad_unit['name']
    }
   }
  ]
  filter_statement = {'query': 'WHERE parentId = :id AND name = :auName','values': values}
  tempSLAU = inventory_service.GetAdUnitsByStatement(filter_statement)
  if tempSLAU[0]['totalResultSetSize'] != "0":
    warningLogFile = open(warningLog,'a')
    message = "%s,%s,Ad unit already exists\n"  % (level, ad_unit['name'])
    warningLogFile.write(message)
    warningLogFile.close()
    return 0
  else:
    return 1

 
  
def Main():
  # Clean old logs
  f = open(warningLog,'w')
  f.write("Level,Ad Unit, Message\n")
  f.close()
  f = open(errorLog,'w')
  f.write("Level,Ad Unit,Message\n")
  f.close()
  f = open(successLog,'w')
  f.write("Parent ID, ID, Ad Unit\n")
  f.close()

  # Read the ad units from the text file
  args = sys.argv[1:]
  auFile = args[0]
  adUnits = getFileAdUnits(auFile,1)
  # Create the ad units
  print "Creation begins here for the 5 levels"
  if len(adUnits) != 0:
    createAUs(adUnits,1)
  print "First level done"
  
  adUnits = getFileAdUnits(auFile,0)
  if len(adUnits) != 0:
    createAUs(adUnits,2)
  print "Second level done"
  
  adUnits = getFileAdUnits(auFile,0)
  if len(adUnits) != 0:
    createAUs(adUnits,3)
  print "Third level done"
  
  adUnits = getFileAdUnits(auFile,0)
  if len(adUnits) != 0:
    createAUs(adUnits,4)
  print "Fourth level done"

  adUnits = getFileAdUnits(auFile,0)
  if len(adUnits) != 0:
    createAUs(adUnits,5)
  print "Fifth level done"


if __name__ == '__main__':
  Main()
 

